.. _глава_61_start:

Универсальный синтаксис вызова функций (UFCS)
=============================================

.. index::
    универсальный синтаксис вызова функций
    UFCS

UFCS - это функция, которая применяется компилятором автоматически. Она позволяет использовать
синтаксис функций-членов даже для обычных функций. Это можно объяснить просто
сравнением двух выражений:

.. code-block:: d

    variable.foo(arguments)

Когда компилятор встречает выражение, подобное приведенному выше, если не существует
функции-члена с именем foo, которая может быть вызвана на переменной с заданными
аргументами, то компилятор также пытается скомпилировать следующее выражение:

.. code-block:: d

    foo(variable, arguments)

Если это новое выражение действительно может быть скомпилировано, то компилятор просто принимает его
это выражение. В результате, хотя foo(), очевидно, была обычной функцией, она становится
принята для использования синтаксисом функции-члена.
*Примечание: UFCS рассматривает только функции, определенные в области видимости модуля; например,
вложенные функции* (:ref:`Вложенные функции, структуры и классы<Вложенные функции, структуры и классы>`) *не могут быть вызваны с помощью синтаксиса UFCS.*
Мы знаем, что функции, тесно связанные с типом, определяются как
функции-члены этого типа. Это особенно важно для инкапсуляции, поскольку
только функции-члены типа (и модуль этого типа) могут получить доступ к его
частным членам.
Рассмотрим класс Car, который хранит количество топлива:

.. code-block::

    class Car
    {
        enum economy = 12.5; // kilometers per liter (average)
        private double fuelAmount; // liters
        this(double fuelAmount)
        {
            this.fuelAmount = fuelAmount;
        }

        double fuel() const
        {
            return fuelAmount;
        }
        // ...
    }


Хотя функции-члены очень полезны и иногда необходимы, не каждая
не каждая функция, работающая с типом, должна быть функцией-членом. Некоторые операции
над типом слишком специфичны для определенного приложения, чтобы быть функциями-членами. Например,
например, функция, определяющая, может ли автомобиль проехать определенное расстояние
может быть определена как обычная функция:

.. code-block:: d

    bool canTravel(Car car, double distance)
    {
        return (car.fuel() * car.economy) >= distance;
    }

Это, естественно, приводит к расхождению при вызове функций, связанных с типом:
объекты появляются в разных местах в этих двух синтаксисах:

.. code-block:: d

    void main()
    {
        auto car = new Car(5);
        auto remainingFuel = car.fuel(); // Member function syntax
        if (canTravel(car, 100))
        { // Regular function syntax
            // ...
        }
    }

UFCS устраняет это несоответствие, позволяя вызывать обычные функции с помощью
синтаксис функции-члена:

.. code-block:: d

    if (car.canTravel(100)) // Regular function, called by the
    {
        // member function syntax
        // ...
    }

Эта возможность доступна и для фундаментальных типов, включая литералы:

.. code-block:: d

    int half(int value)
    {
        return value / 2;
    }

    void main()
    {
        assert(42.half() == 21);
    }

Как мы увидим в следующей главе, когда нет аргументов для передачи в функцию
функция может быть вызвана без круглых скобок. Когда эта функция
выражение выше становится еще короче. Все три следующих
выражения эквивалентны:

.. code-block:: d

    result = half(value);
    result = value.half();
    result = value.half;

UFCS особенно полезен при цепочке вызовов функций. Давайте посмотрим это на группе
функций, оперирующих с фрагментами int:

.. code-block:: d

    // Returns the result of dividing all of the elements by
    // 'divisor'
    int[] divide(int[] slice, int divisor)
    {
        int[] result;
        result.reserve(slice.length);
        foreach (value; slice)
        {
            result ~= value / divisor;
        }
        return result;
    }

    // Returns the result of multiplying all of the elements by
    // 'multiplier'
    int[] multiply(int[] slice, int multiplier)
    {
        int[result;
            result.reserve(slice.length);
            foreach (value; slice)
            {
                result ~= value * multiplier;
            }
            return result;
            }

            // Filters out elements that have odd values
            int[] evens(int[] slice)
            {
                int[] result;
                result.reserve(slice.length);
                foreach (value; slice)
                {
                    if (!(value % 2))
                    {
                        result ~= value;
                    }
                }
                return result;
            }
    }

При записи обычным синтаксисом, без использования UFCS, выражение
выражение, цепляющее три вызова этих функций, можно записать, как в
следующей программе:

.. code-block:: d

    import std.stdio;
    // ...
    void main()
    {
        auto values = [ 1, 2, 3, 4, 5 ];
        writeln(evens(divide(multiply(values, 10), 3)));
    }

Значения сначала умножаются на 10, затем делятся на 3, и, наконец, используются только четные
используются только четные числа:

.. code-block:: console

    [6, 10, 16]

Проблема с приведенным выше выражением заключается в том, что, хотя пара умножения на
10 связаны, а пара деления на 3 связаны, части каждой пары в конечном итоге
записываются отдельно друг от друга. UFCS устраняет эту проблему и обеспечивает более
естественный синтаксис, отражающий фактический порядок операций:

.. code-block:: d

    writeln(values.multiply(10).divide(3).evens);

Некоторые программисты используют преимущества UFCS даже для таких вызовов, как writeln():

.. code-block:: d

    values.multiply(10).divide(3).evens.writeln;

В качестве примечания, всю приведенную выше программу можно было бы написать гораздо проще
с помощью map() и filter():

.. code-block:: d

    import std.stdio;
    import std.algorithm;

    void main()
    {
        auto values = [ 1, 2, 3, 4, 5 ];
        writeln(values.map!(a => a * 10).map!(a => a / 3).filter!(a => !(a % 2)));
    }

В приведенной выше программе используются преимущества шаблонов (:ref:`Шаблоны<глава_64_start>`), диапазонов (:ref:`Диапазоны<глава_81_start>`),
и лямбда-функции (:ref:`Указатели на функции, делегаты и лямбда-выражения<глава_72_start>`), о которых будет рассказано в последующих главах.
